package com.ingenico.insider.services.impl;

import java.awt.geom.Point2D;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.MessageFormat;
import java.util.logging.Logger;

import com.ingenico.insider.nodes.PCurvePath;
import com.ingenico.insider.nodes.PMark;
import com.ingenico.insider.nodes.PSampleChannel;
import com.ingenico.insider.util.Constants;

import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.event.PBasicInputEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.util.PPickPath;

public final class CoordinatesSupplier {
	private static final Logger _logger = Logger.getLogger(CoordinatesSupplier.class.getCanonicalName());

	public static final String MAX_COORDS_LINES_PATH = Constants.INSIDER_BASE + Constants.SEPARATOR + "max_coordinates_lines";
	public static final String UNIT_FORMAT = Constants.INSIDER_BASE + Constants.SEPARATOR + "unit_format";
	public static final String UNIT_MULTIPLIER = Constants.INSIDER_BASE + Constants.SEPARATOR + "unit_multiplier";

	protected String unitFormat = UserPreferencesSupplier.getInstance().getString(UNIT_FORMAT);

	/**
	 * Multiplier to convert from a second to our unit
	 * for instance, milliseconds would use 1000 because 1 ms = 1000 s
	 */
	protected double unitMultiplier = UserPreferencesSupplier.getInstance().getDecimal(UNIT_MULTIPLIER).intValue();;

	/**
	 * singleton instance
	 */
	private static CoordinatesSupplier instance;

	/**
	 * private constructor of final class to prevent external instantiation
	 */
	private CoordinatesSupplier() {
		final PCamera camera = CanvasSupplier.getInstance().getCanvas().getCamera();
		camera.addInputEventListener(new PBasicInputEventHandler() {
			private final int maxCoordsLines = UserPreferencesSupplier.getInstance().getDecimal(MAX_COORDS_LINES_PATH).intValue();

			public void mouseMoved(PInputEvent event) {
				updateToolTip(event);
			}

			public void mouseDragged(PInputEvent event) {
				updateToolTip(event);
			}

			public void updateToolTip(PInputEvent event) {
				PNode currentPickedNode;
				int coordsLineCounter;

				final DecimalFormat xAxisFormat = new DecimalFormat("#,##0");
				final DecimalFormat yAxisFormat = new DecimalFormat("#,##0.000");

				final StringBuffer buf = new StringBuffer();
				final PPickPath path = event.getInputManager().getMouseOver();
				currentPickedNode = path.getPickedNode();
				coordsLineCounter = 0;
				while ((currentPickedNode != null) && (coordsLineCounter++ < maxCoordsLines) ) {
					_logger.fine("found picked node : " + currentPickedNode);
					final Point2D p = (Point2D)event.getPosition().clone();
					currentPickedNode.globalToLocal(p);

					if (currentPickedNode instanceof PCurvePath) {
						if ( ! buf.toString().isEmpty()) {
							buf.append("\n");
						}
						final double x = p.getX();
						buf.append(
							currentPickedNode.getParent().getParent().toString()
							+ " [ x=" + xAxisFormat.format(x)
							+ " ; y=" + yAxisFormat.format(p.getY())
							+ " ]");
						try {
							final double sampleRate = ((PSampleChannel)currentPickedNode.getParent().getParent()).getDataChannel().getSampleRate();
							if (sampleRate > 0) {
								buf.append(", t = " + MessageFormat.format(unitFormat, x * unitMultiplier / sampleRate));
							}							
						} catch (IOException e) {
// TODO Auto-generated catch block... what can we do here ? when can this happen ?
							e.printStackTrace();
						}
					} else if (currentPickedNode instanceof PMark) {
						if ( ! buf.toString().isEmpty()) {
							buf.append("\n");
						}
						buf.append(((PMark)currentPickedNode).getMarkName() + " [ dX=" + xAxisFormat.format(currentPickedNode.getWidth()) + " ; dY=" + yAxisFormat.format(currentPickedNode.getHeight()) + " ], ");
					} else {
//						if ( ! buf.toString().isEmpty()) {
//							buf.append("\n");
//						}
//						buf.append(currentPickedNode.getClass().getSimpleName() + " [ x=" + xAxisFormat.format(p.getX()) + " ]");
					}
					currentPickedNode = path.nextPickedNode();
				};

				// Finally update the notification area if required...
				if ( ! buf.toString().isEmpty()) {
					NotificationSupplier.getInstance().setUserText(buf.toString());
					NotificationSupplier.getInstance().setVisible(true);
				} else {
					NotificationSupplier.getInstance().setVisible(false);
				}
			}
		});
	}

	/**
	 * retrieve the singleton instance
	 *
	 * @return the CoordinatesSupplier singleton
	 */
	public static CoordinatesSupplier getInstance() {
		if (instance == null) {
			_logger.info(CoordinatesSupplier.class.getName() + " instance does not exist... Creating instance.");
			instance = new CoordinatesSupplier();
		}
		return instance;
	}	
}
